import { BaseComp } from '../../../common/component/comp';
import { ZRenderComp } from '../../../common/component/zrComp';

import { FlightItem } from '../../compute/flightRecords';
import layout from './layouts';

/**
 * FlightRecordView
 * ├── Background (z-index=-1)
 * ├── Row (z-index=1,11)
 * ├── SummaryRow (z-index=1,11)
 */

/**
 * 形状和工具方法 (utils) 都放这里了哈
 */
let Parallelogram;
function initParallelogram(zrender) {
  Parallelogram = zrender.Path.extend({
    type: 'parallelogram',
    shape: {
      // x, y 原点在箭头的凹槽处
      x: 0,
      y: 0,
      width: 20,
      height: 20,
      offset: 10,
    },
    buildPath: function (path, shape) {
      let x = shape.x;
      let y = shape.y;
      let w = shape.width;
      let h = shape.height;
      let o = shape.offset;

      path.lineTo(x + o, y);
      path.lineTo(x + o + w, y);
      path.lineTo(x + w, y + h);
      path.lineTo(x, y + h);
      path.lineTo(x + o, y);
      path.closePath();
    },
    style: {
      // stroke: 'white',
      // lineWidth: 2,
      fill: 'black'
    }
  });
}

class FlightRecordView extends BaseComp {
  constructor(args) {
    super(null);
    this.context = args.context;
    /** @type {HTMLElement} */ this.dom = args.dom;
    this.zrender = args.zrender;
    initParallelogram(this.zrender);
    this.zr = this.zrender.init(this.dom);
    this.defer(() => this.zr.dispose());

    this.bg = new Background({ parent : this, zr : this.zr, zrender : this.zrender });
    /** @type {Row[]} */
    this.rows = [];
    /** @type {SummaryRow[]} */
    this.summaryRows = [];

    // style
    this.styleConfig = null;
  }

  loadStyleConfig() {
    if (this.styleConfig) {
      return Promise.resolve(this.styleConfig);
    }
    return fetch(this.context.assetPath('data/flightRecords/ver2021Style.json'))
      .then(res => (res.ok) ? res.json() : null)
      .then(data => this.styleConfig = data);
  }

  /**
   * @param {FlightItem[]} items 
   */
  setRecords(items) {
    this.rows.forEach(row => row.destroy());
    this.rows = [];
    this.summaryRows.forEach(row => row.destroy());
    this.summaryRows = [];

    if (items.length == 0) {
      return;
    }

    this.loadStyleConfig()
      .then(_ => this.recreateRows(items));
  }

  /**
   * @param {FlightItem[]} items 
   */
  recreateRows(items) {
    // 为每一个 rows 设置位置
    // 如果前后的 year 不同的话, 中间塞一个标签行
    let lastY = layout.PX_TOP;
    let lastYear = 0;
    
    for (let i = 0; i < items.length; i++) {
      const record = items[i];
      
      if (record.year != lastYear) {
        lastYear = record.year;
        
        // 创建 summary 行
        let srow = new SummaryRow({
          parent : this,
          zr : this.zr,
          zrender : this.zrender,
          year: lastYear,
        });
        this.summaryRows.push(srow);
        srow.y = lastY;
        lastY += srow.height;
      }
      
      let row = new Row({
        parent : this,
        zr : this.zr,
        zrender : this.zrender,
        record,
        colors : this.styleConfig.colors,
      });
      this.rows.push(row);
      row.y = lastY;
      lastY += row.height;
    }

    this.resizeForAllLayers(layout.PANEL_WIDTH, lastY + layout.FOOTER_HEIGHT);
  }

  resizeForAllLayers(width, height) {
    this.dom.style.width = width + 'px';
    this.dom.style.height = height + 'px';

    this.zr.resize({ width, height });
    let findCanvasAndResize = function(el) {
      if (el instanceof HTMLCanvasElement) {
        el.width = width;
        el.height = height;
        el.style.width = width + 'px';
        el.style.height = height + 'px';
      } else if (el instanceof HTMLDivElement) {
        let children = el.children;
        for (let i = 0; i < children.length; i++) {
          findCanvasAndResize(children[i]);
        }
      }
    }

    // 这可能是 bug, 需要手动将 canvas 重置大小, 而且重置之后 refresh 都不能重绘, 必须改变一下它元素的属性
    findCanvasAndResize(this.dom);
    this.emit('view/resize', { width, height });
  }

  /**
   * @param {string} color
   */
  setBgColor(color) {
    this.bg.setBgColor(color);
  }
}

class Background extends ZRenderComp {
  constructor(args) {
		super(args.parent, args.zrender);
    this.useExistZRenderInstance(args.zr, 0, 0);

    this.on('view/resize', (_, value) => {
      this.onResize(value);
    });
  }

  init() {
    this.black = this.createRect(layout.PANEL_WIDTH, 1000)
      .fill('#FFF')
      .z(0, -10)
      .build();
  }

  onResize(args) {
    if (!this.hasInited) return;
    this.black.rectShape().height(args.height).update();
  }

  setBgColor(color) {
    if (!this.hasInited) return;
    this.black.style().fill(color).update();
  }
}

class Row extends ZRenderComp {
  constructor(args) {
    super(args.parent, args.zrender);
    this.useExistZRenderInstance(args.zr, 0, 0);
    /** @type {FlightItem} */ this.record = args.record;
    this.colors = args.colors;

    // 行间距
    this.height = layout.PX_SUMMARY_LINE_W_OFF;
  }

  init() {
    this.drawDay();
    this.drawVehicle();
    this.drawOrbit();
    this.drawPayload();
    this.drawSite();
  }

  drawDay() {
    const {PX_DAY_L, PX_DAY_W, PX_DAY_OFF} = layout;
    const {record, colors} = this;
    const {month, day} = this.record;
    const text = `${month > 9 ? month + '' : '0' + month}-${day > 9 ? day + '' : '0' + day}`;

    this.createCustomShape(Parallelogram, {
        width: PX_DAY_W,
        height: layout.PX_DAY_H,
        offset: PX_DAY_OFF })
      .position(PX_DAY_L, 0)
      .fill(record.success ? colors.months[record.month] : colors.months.fail)
      .z(0, 1)
      .build();
    this.createText(text, 24)
      .textFill(colors.payloadsText[0])
      .textStyle('center', 'bottom', 'normal', 'Arial Black')
      .position(PX_DAY_L + (PX_DAY_W + PX_DAY_OFF) / 2, layout.PX_DAY_TX_Y)
      .z(0, 11)
      .build();
  }

  drawVehicle() {
    const { colors, record } = this;
    // vehicleColors

    /** @type {string} */ let strokeColor;
    /** @type {string} */ let bgColor;
    /** @type {string} */ let textColor;
    if (record.success) {
      let name = record.isManned() ? 'for.manned' : record.vehicles[0].name;
      strokeColor = colors.vehiclesStroke[name];
      strokeColor = (strokeColor == null ? colors.vehiclesStroke.other : strokeColor);
      textColor = strokeColor;
      bgColor = record.isImportant() ? colors.vehiclesBg[name] : null;
      bgColor = bgColor == null ? colors.vehiclesBg.other : bgColor;
    } else {
      strokeColor = colors.vehiclesStroke.FTO;
      bgColor = colors.vehiclesBg.FTO;
      textColor = colors.payloadsText[0];
    }

    const {PX_VEH_W, PX_DAY_H, PX_DAY_OFF, PX_VEH_L, PX_VEH_ST_W} = layout;

    this.createCustomShape(Parallelogram, {
        width: PX_VEH_W,
        height: PX_DAY_H,
        offset: PX_DAY_OFF })
      .position(PX_VEH_L, 0)
      .fill(bgColor)
      .z(0, 1)
      .build();
    this.createCustomShape(Parallelogram, {
        width: PX_VEH_ST_W,
        height: PX_DAY_H,
        offset: PX_DAY_OFF })
      .position(PX_VEH_L, 0)
      .fill(strokeColor)
      .z(0, 2)
      .build();
    this.createCustomShape(Parallelogram, {
        width: PX_VEH_ST_W,
        height: PX_DAY_H,
        offset: PX_DAY_OFF })
      .position(PX_VEH_L + PX_VEH_W - PX_VEH_ST_W, 0)
      .fill(strokeColor)
      .z(0, 2)
      .build();

    // text
    const {PX_VEH_TX_Y, PX_VEH_TX_W} = layout;
    
    let y2 = PX_VEH_TX_Y;
    let centerX = PX_VEH_L + (PX_VEH_W + PX_DAY_OFF) / 2;
    const maxWidth = PX_VEH_TX_W * 0.96;
    
    if (record.vehicles.length == 1) {
      if (record.vehicles[0].id == "") {
        const spTx1 = this.createText(record.vehicles[0].name, 24)
          .textStyle('center', 'bottom', 'normal', 'Arial Black')
          .position(centerX, y2)
          .textFill(textColor)
          .z(0, 1)
          .build();
        // 如果宽太大, 需要缩小
        let w = spTx1.getBoundingRect().width;
        if (w > maxWidth) {
          spTx1.style().scale(maxWidth / w, 1).update();
        }
      } else {
        const leftX = centerX - maxWidth / 2;
        const spTx1 = this.createText(record.vehicles[0].name, 24)
          .textStyle('left', 'bottom', 'normal', 'Arial Black')
          .position(leftX, y2)
          .textFill(textColor)
          .z(0, 1)
          .build();
        const spTx2 = this.createText(" " + record.vehicles[0].id, 14)
          .textStyle('left', 'bottom', 'normal', 'Arial Black')
          .position(leftX, y2)
          .textFill(textColor)
          .z(0, 1)
          .build();
        
        let w1 = spTx1.getBoundingRect().width;
        let w2 = spTx2.getBoundingRect().width;
        if (w1 + w2 > maxWidth) {
          let scale = maxWidth / (w1 + w2);
          spTx1.style().scale(scale, 1).update();
          spTx2.style().scale(scale, 1).update();
          spTx2.moveTo(scale * w1 + leftX, y2);
        } else {
          let offset = (maxWidth - w1 - w2) / 2;
          spTx1.moveTo(leftX + offset, y2);
          spTx2.moveTo(leftX + offset + w1, y2);
        }
      }
    } else {
      this.createText(`${record.vehicles[0].name} ${record.vehicles[0].id}`, 16)
        .textStyle('center', 'bottom', 'normal', 'Arial Black')
        .position(centerX, layout.PX_VEH_TX_YB1)
        .textFill(textColor)
        .z(0, 1)
        .build();
      this.createText(`${record.vehicles[1].name} ${record.vehicles[1].id}`, 14)
        .textStyle('center', 'bottom', 'normal', 'Arial Black')
        .position(centerX, layout.PX_VEH_TX_YB2)
        .textFill(textColor)
        .z(0, 1)
        .build();
    }
  }

  drawOrbit() {
    const { record, colors } = this;

    /** @type {string} */ let bgColor;
    /** @type {string} */ let textColor = colors.payloadsText[0];
    if (record.success) {
      bgColor = colors.orbits[record.orbit];
      bgColor = bgColor == null ? colors.orbits.EEO : bgColor;
    } else {
      bgColor = colors.orbits.FTO;
    }

    const {PX_OBT_L, PX_OBT_W, PX_DAY_H, PX_DAY_OFF, PX_OBT_TX_Y} = layout;

    this.createCustomShape(Parallelogram, {
        width: PX_OBT_W,
        height: PX_DAY_H,
        offset: PX_DAY_OFF })
      .position(PX_OBT_L, 0)
      .fill(bgColor)
      .z(0, 1)
      .build();

    if (record.orbitParam == "") {
      this.createText(record.orbit, 24)
        .textStyle('center', 'bottom', 'normal', 'Arial Black')
        .position(PX_OBT_L + (PX_OBT_W + PX_DAY_OFF) / 2, PX_OBT_TX_Y)
        .textFill(textColor)
        .z(0, 11)
        .build();
    } else {
      const {PX_OBT_TX_YB1, PX_OBT_TX_YB2} = layout;
      this.createText(record.orbit, 20)
        .textStyle('center', 'bottom', 'normal', 'Arial Black')
        .position(PX_OBT_L + (PX_OBT_W + PX_DAY_OFF) / 2, PX_OBT_TX_YB1)
        .textFill(textColor)
        .z(0, 11)
        .build();
      const sp2 = this.createText(record.orbitParam, 16)
        .textStyle('center', 'bottom', 'normal', 'Arial Black')
        .position(PX_OBT_L + (PX_OBT_W + PX_DAY_OFF) / 2, PX_OBT_TX_YB2)
        .textFill(textColor)
        .z(0, 11)
        .build();
      if (sp2.getBoundingRect().width > PX_OBT_W) {
        sp2.style().scale(PX_OBT_W / sp2.getBoundingRect().width, 1).update();
      }
    }
  }

  drawPayload() {
    const {record, colors} = this;

    /** @type {string} */ let bgColor;
    /** @type {string} */ let textColor;
    if (record.success) {
      let name = record.isManned() ? 'for.manned' : record.vehicles[0].name;
      bgColor = colors.payloadsBg[name];
      bgColor = bgColor == null ? colors.payloadsBg.other : bgColor;
      textColor = (record.flag & 1) ? colors.payloadsText[1] : colors.payloadsText[0];
    } else {
      bgColor = colors.payloadsBg.FTO;
      textColor = colors.payloadsText[0];
    }

    const {PX_PLD_W, PX_DAY_H, PX_DAY_OFF, PX_PLD_L, PX_PLD_TX_X, PX_PLD_TX_Y, PX_MAS_TX_Y, PX_PLD_TX_W} = layout;
    const TX_X = PX_PLD_L + PX_PLD_TX_X;

    this.createCustomShape(Parallelogram, {
        width: PX_PLD_W,
        height: PX_DAY_H,
        offset: PX_DAY_OFF })
      .position(PX_PLD_L, 0)
      .fill(bgColor)
      .z(0, 1)
      .build();

    // text 文字后面要移动
    const spTx = this.createText(record.payloads.join('  '), 24)
      .textStyle('left', 'bottom', 'normal', 'Arial Black')
      .position(TX_X, PX_PLD_TX_Y)
      .textFill(textColor)
      .z(0, 11)
      .build();


    // mass / 部分成功
    let spMsTx = null;
    if (record.mass != "" || record.isPartialSuccess()) {
      spMsTx = this.createText(record.isPartialSuccess() ? '部分成功' : record.mass, 18)
        .textStyle('right', 'bottom', 'oblique', 'Arial Black')
        .position(PX_PLD_L + PX_PLD_W, PX_MAS_TX_Y)
        .textFill(colors.payloadsText[0])
        .z(0, 11)
        .build();
    }
    let left = spTx.getBoundingRect().width;
    let right = spMsTx == null ? 0 : spMsTx.getBoundingRect().width;
    if (left + right <= PX_PLD_TX_W) {
      return;
    }

    let scale = PX_PLD_TX_W / (left + right);
    if (scale > 0.6) {
      // 压缩饼干方法
      spTx.style().scale(scale, 1).update();
      if (spMsTx != null) {
        spMsTx.style().scale(scale, 1).update();
      }
      return;
    }

    // 字太多, 干脆分两行写, 左边字号变小
    // 如果有多个载荷, 一个载荷最好在一行
    if (spMsTx != null) {
      spMsTx.style().scale(0.75, 1).update();
    }
    let leftTo = PX_PLD_TX_W - right * 0.75;

    if (record.payloads.length == 1) {
      spTx.moveTo(PX_PLD_L + PX_PLD_TX_X, 5);
      spTx.style()
        .fontSize(14)
        .textRect(0, 0, leftTo, 400)
        .textStyle(null, null, 'normal', 'Arial Black')
        .update();
      // TODO 未完成
      console.error("record.payloads.length == 1  TODO");
      return;
    }
    spTx.destroy();

    const spTxs = record.payloads.map(tx => this.createText(tx, 16)
      .textStyle('left', 'bottom', 'normal', 'Arial Black')
      .position(0, 0)
      .textFill(textColor)
      .z(0, 11)
      .build());
    const pTxWs = spTxs.map(sp => sp.getBoundingRect().width);
    const TX_OFF = 12; // 每个 record 间距
    let line1Used = 0, line2Used = 0, line1Count = 0, line2Scale = 1;

    for (let i = 0; i < pTxWs.length; i++) {
      let pTxW = pTxWs[i];
      if (line1Used + pTxW <= leftTo) {
        line1Used += pTxW;
        line1Used += TX_OFF;
        line1Count++;
      } else {
        break;
      }
    }

    for (let i = line1Count; i < pTxWs.length; i++) {
      let pTxW = pTxWs[i];
      if (line2Used != 0) {
        line2Used += TX_OFF;
      }
      line2Used += pTxW;
    }

    if (line2Used > leftTo) {
      line2Scale = leftTo / line2Used;
    }

    line1Used = line2Used = 0;
    const {PX_PLD_TX_YB1, PX_PLD_TX_YB2} = layout;

    for (let i = 0; i < line1Count; i++) {
      line1Used = (i > 0) ? line1Used += TX_OFF : line1Used;
      spTxs[i].moveTo(TX_X + line1Used, PX_PLD_TX_YB1);
      line1Used += pTxWs[i];
    }
    for (let i = line1Count; i < spTxs.length; i++) {
      line2Used = (i > line1Count) ? line2Used += TX_OFF * line2Scale : line2Used;
      spTxs[i].moveTo(TX_X + line2Used, PX_PLD_TX_YB2);
      spTxs[i].style().scale(line2Scale, 1).update();
      line2Used += pTxWs[i] * line2Scale;
    }
    // 如果左边文本长度够, 右边的 mass 文本长度可以扩展
    if (line2Scale != 1 || spMsTx == null) {
      return;
    }
    let leftUsed = Math.max(line1Used, line2Used);
    scale = (PX_PLD_TX_W - leftUsed) / right;
    spMsTx.style().scale(scale > 1 ? 1 : scale, 1).update();
  }

  drawSite() {
    const {record, colors} = this;

    /** @type {string} */ let bgColor;
    /** @type {string} */ const textColor = colors.payloadsText[0];
    if (record.success) {
      bgColor = colors.sites[record.site];
      bgColor = (bgColor == null ? colors.sites.other : bgColor);
    } else {
      bgColor = colors.sites.FTO;
    }

    const {PX_SIT_L, PX_SIT_W, PX_DAY_H, PX_DAY_OFF, PX_SIT_TX_Y} = layout;

    this.createCustomShape(Parallelogram, {
        width: PX_SIT_W,
        height: PX_DAY_H,
        offset: PX_DAY_OFF })
      .position(PX_SIT_L, 0)
      .fill(bgColor)
      .z(0, 1)
      .build();
    this.createText(record.site, 24)
      .textStyle('center', 'bottom', 'oblique', 'Arial Black')
      .position(PX_SIT_L + (PX_SIT_W + PX_DAY_OFF) / 2 - 2, PX_SIT_TX_Y)
      .textFill(textColor)
      .z(0, 11)
      .build();
  }
}

class SummaryRow extends ZRenderComp {
  constructor(args) {
    super(args.parent, args.zrender);
    this.useExistZRenderInstance(args.zr, 0, 0);
    /** @type {number} */ this.year = args.year;

    // 行间距
    this.height = layout.PX_LINE_W_OFF;
  }

  init() {
    const { PX_DAY_H, PX_DAY_OFF, PX_DAY_L, PX_YL_M1_W } = layout;

    this.createCustomShape(Parallelogram, {
        width: PX_YL_M1_W,
        height: PX_DAY_H,
        offset: PX_DAY_OFF })
      .position(PX_DAY_L, 0)
      .fill(layout.CL_YL_M1)
      .z(0, 2)
      .build();
    this.createCustomShape(Parallelogram, {
        width: layout.PX_YL_M2_W,
        height: PX_DAY_H / 5,
        offset: PX_DAY_OFF / 5 })
      .position(PX_DAY_L + PX_YL_M1_W, PX_DAY_H*0.8)
      .fill(layout.CL_YL_M2)
      .z(0, 1)
      .build();

    let x = PX_DAY_L + (PX_YL_M1_W + PX_DAY_OFF) / 2;
    this.createText(`${this.year}`, 28)
      .textFill(layout.CL_YL_TX)
      .textStyle('center', 'bottom', 'oblique', 'Arial Black')
      .position(x, layout.PX_YL_TX_Y)
      .z(0, 11)
      .build();
  }
}

export {
  FlightRecordView
};